#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @Project:     data_manager
# @File:        cli.py
# @Author:      Fan GuiChuan
# @Date:        2024/12/17 9:16
# @Version:     Python3.7
# @Description:
import json
import logging

import click
import peewee
import yaml
from playhouse.reflection import Introspector

from database_manager.utils import load_config
from peewee import SQL

from tabulate import tabulate

from .models import generate_models_from_database, get_database, get_tables

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


@click.group()
def cli():
    pass


@cli.command()
@click.option('--dbname', required=True, help='数据库名称')
@click.option('--config', required=True, help='YAML 配置文件路径')
def list_tables(dbname, config):
    """列出数据库中的所有表"""
    logger.info(f"开始列出数据库中的表：数据库名称={dbname}")

    try:
        # 加载配置文件
        config_data = load_config(config)
        logger.info(f"配置文件加载成功：{config}")

        # 获取具体的数据库配置
        db_config = config_data.get(dbname)
        if not db_config:
            logger.error(f"数据库配置未找到：{dbname}")
            click.echo(f"数据库配置未找到：{dbname}")
            return

        # 获取数据库连接
        db = get_database(db_config)
        logger.info(f"数据库连接成功：{dbname}")

        # 获取所有表的名称
        tables = get_tables(db)
        logger.info(f"获取表成功，表的数量：{len(tables)}")

        # 输出表的名称
        click.echo("数据库中的表：")
        for table in tables:
            click.echo(f"- {table}")

    except Exception as e:
        logger.error(f"列出表失败：{e}")
        click.echo(f"列出表失败：{e}")


@cli.command()
@click.option('--dbname', required=True, help='数据库名称')
@click.option('--config', required=True, help='YAML 配置文件路径')
@click.option('--table', required=True, help='表名')
@click.option('--data', required=True, help='插入的数据（JSON 格式）')
def insert(dbname, config, table, data):
    """插入数据到表中"""
    logger.info(f"开始插入：数据库名称={dbname}, 表={table}, 数据={data}")

    try:
        # 加载配置文件
        config_data = load_config(config)
        logger.info(f"配置文件加载成功：{config}")

        # 获取具体的数据库配置
        db_config = config_data.get(dbname)
        if not db_config:
            logger.error(f"数据库配置未找到：{dbname}")
            click.echo(f"数据库配置未找到：{dbname}")
            return

        # 获取数据库连接
        db = get_database(db_config)
        logger.info(f"数据库连接成功：{dbname}")

        # 动态生成模型
        introspector = Introspector.from_database(db)
        models = introspector.generate_models()
        Model = models.get(table)
        if not Model:
            logger.error(f"表 {table} 不存在")
            click.echo(f"表 {table} 不存在")
            return

        # 解析插入数据
        try:
            data_dict = json.loads(data)
        except json.JSONDecodeError:
            logger.error("插入数据格式不正确，应为 JSON 格式")
            click.echo("插入数据格式不正确，应为 JSON 格式")
            return

        # 检查字段是否存在
        for field in data_dict.keys():
            if not hasattr(Model, field):
                logger.error(f"字段 {field} 不存在")
                click.echo(f"字段 {field} 不存在")
                return

        # 插入数据
        try:
            with db.atomic():  # 使用事务确保数据一致性
                result = Model.create(**data_dict)
            logger.info(f"插入成功，插入的记录 ID：{result.id}")
            click.echo(f"插入成功，插入的记录 ID：{result.id}")
        except Exception as e:
            logger.error(f"插入失败：{e}")
            click.echo(f"插入失败：{e}")

    except Exception as e:
        logger.error(f"插入失败：{e}")
        click.echo(f"插入失败：{e}")


@cli.command()
@click.option('--dbname', required=True, help='数据库连接配置名称')
@click.option('--config', required=True, help='数据库配置文件路径，包含所有的数据库连接配置')
@click.option('--table', required=True, help='表名')
@click.option('--fields', default='*', help='查询的字段（逗号分隔）')
@click.option('--where', default=None, help='查询条件（如 "username=\'Alice\'"）')
@click.option('--order-by', default=None, help='排序字段（如 "id DESC"）')
@click.option('--limit', default=None, type=int, help='每页记录数')
@click.option('--offset', default=0, type=int, help='偏移量')
@click.option('--format', default='table', type=click.Choice(['table', 'json', 'csv']), help='输出格式')
def query(dbname, config, table, fields, where, order_by, limit, offset, format):
    """通用查询命令"""
    # 连接数据库
    logger.info(
        f"开始查询：数据库名称={dbname}, 表={table}, 字段={fields}, 条件={where}, 排序={order_by}, 分页={limit}/{offset}, 格式={format}")
    # 加载配置文件
    config_data = load_config(config)
    logger.info(f"配置文件加载成功：{config}")

    # 获取具体的数据库配置
    db_config = config_data.get(dbname)
    if not db_config:
        logger.error(f"数据库配置未找到：{dbname}")
        click.echo(f"数据库配置未找到：{dbname}")
        return
    models = generate_models_from_database(get_database(db_config))
    Model: peewee.Model = models.get(table)
    if not Model:
        click.echo(f"表 {table} 不存在")
        return

    # 解析查询字段
    if fields == '*':
        query_fields = [Model]
    else:
        query_fields = [getattr(Model, field) for field in fields.split(',')]

    # 构建查询对象
    query = Model.select(*query_fields)

    # 添加 WHERE 条件
    if where:
        query = query.where(SQL(where))

    # 添加 ORDER BY 条件
    if order_by:
        order_by_fields = [SQL(field) for field in order_by.split(',')]
        query = query.order_by(*order_by_fields)

    # 添加分页条件
    if limit:
        query = query.limit(limit).offset(offset)

    # 执行查询并转换为字典
    results = list(query.dicts())
    logger.info(f"查询成功，返回 {len(results)} 条记录")

    # 过滤掉未查询的字段
    if fields != '*':
        filtered_results = [{field: row[field] for field in fields.split(',')} for row in results]
    else:
        filtered_results = results

    # 输出结果
    if format == 'table':
        # 表格格式
        headers = [field for field in fields.split(',')] if fields != '*' else [field.name for field in
                                                                                Model._meta.fields.values()]
        rows = [[row.get(field) for field in headers] for row in filtered_results]
        click.echo(tabulate(rows, headers=headers))
    elif format == 'json':
        # JSON 格式
        click.echo(json.dumps(filtered_results, indent=4))
    elif format == 'csv':
        # CSV 格式
        import csv
        import sys
        writer = csv.writer(sys.stdout)
        headers = [field for field in fields.split(',')] if fields != '*' else [field.name for field in
                                                                                Model._meta.fields.values()]
        writer.writerow(headers)
        for row in filtered_results:
            writer.writerow([row.get(field) for field in headers])


@cli.command()
@click.option('--dbname', required=True, help='数据库名称')
@click.option('--config', required=True, help='YAML 配置文件路径')
@click.option('--table', required=True, help='表名')
@click.option('--update-fields', required=True, help='需要更新的字段及其新值（格式：field1=value1,field2=value2）')
@click.option('--where', default=None, help='更新条件（如 "id=1"）')
def update(dbname, config, table, update_fields, where):
    """更新表中的数据"""
    logger.info(f"开始更新：数据库名称={dbname}, 表={table}, 更新字段={update_fields}, 条件={where}")

    try:
        # 加载配置文件
        config_data = load_config(config)
        logger.info(f"配置文件加载成功：{config}")

        # 获取具体的数据库配置
        db_config = config_data.get(dbname)
        if not db_config:
            logger.error(f"数据库配置未找到：{dbname}")
            click.echo(f"数据库配置未找到：{dbname}")
            return

        # 获取数据库连接
        db = get_database(db_config)
        logger.info(f"数据库连接成功：{dbname}")

        # 动态生成模型
        introspector = Introspector.from_database(db)
        models = introspector.generate_models()
        Model = models.get(table)
        if not Model:
            logger.error(f"表 {table} 不存在")
            click.echo(f"表 {table} 不存在")
            return

        # 解析更新字段
        update_fields_dict = {}
        for field_value in update_fields.split(','):
            field, value = field_value.split('=')
            update_fields_dict[field] = value

        # 构建更新查询
        query = Model.update(**update_fields_dict)

        # 添加 WHERE 条件
        if where:
            query = query.where(SQL(where))

        # 执行更新
        result = query.execute()
        logger.info(f"更新成功，受影响的行数：{result}")
        click.echo(f"更新成功，受影响的行数：{result}")

    except Exception as e:
        logger.error(f"更新失败：{e}")
        click.echo(f"更新失败：{e}")


@cli.command()
@click.option('--dbname', required=True, help='数据库名称')
@click.option('--config', required=True, help='YAML 配置文件路径')
@click.option('--table', required=True, help='表名')
@click.option('--where', default=None, help='删除条件（如 "id=1"）')
def delete(dbname, config, table, where):
    """删除表中的数据"""
    logger.info(f"开始删除：数据库名称={dbname}, 表={table}, 条件={where}")

    try:
        # 加载配置文件
        config_data = load_config(config)
        logger.info(f"配置文件加载成功：{config}")

        # 获取具体的数据库配置
        db_config = config_data.get(dbname)
        if not db_config:
            logger.error(f"数据库配置未找到：{dbname}")
            click.echo(f"数据库配置未找到：{dbname}")
            return

        # 获取数据库连接
        db = get_database(db_config)
        logger.info(f"数据库连接成功：{dbname}")

        # 动态生成模型
        introspector = Introspector.from_database(db)
        models = introspector.generate_models()
        Model = models.get(table)
        if not Model:
            logger.error(f"表 {table} 不存在")
            click.echo(f"表 {table} 不存在")
            return

        # 构建删除查询
        query = Model.delete()

        # 添加 WHERE 条件
        if where:
            query = query.where(SQL(where))

        # 执行删除
        result = query.execute()
        logger.info(f"删除成功，受影响的行数：{result}")
        click.echo(f"删除成功，受影响的行数：{result}")

    except Exception as e:
        logger.error(f"删除失败：{e}")
        click.echo(f"删除失败：{e}")


if __name__ == '__main__':
    cli()
